package email;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.mail.FetchProfile;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Store;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.border.TitledBorder;

// The E-mail Client.
public class EmailClient2 extends JFrame {
	static String username;
	static String password;
	static LoginPrompt login;
	static OutgoingMail sendMail;

	private static final long serialVersionUID = 1L;

	// Message table's data model.
	private static CustomMessageTableModel tableModel;

	// Table listing messages.
	private static JTable table;

	// The side panel that displays the email info
	private JPanel infoPanel;

	// The labels used in the infoPanel
	private static ArrayList<JLabel> labels;

	// The text area's used to display the info about an email
	private static ArrayList<JTextArea> sideInfo;

	// The scroll panes used in the side panel
	private static ArrayList<JScrollPane> sideScroll;

	// The messages that are associated with the open tabs
	public static ArrayList<ReceivedMessage> tabMessages;

	// close tabbed pane for emails @lance staley
	private JTabbedPaneWithCloseIcons tab = new JTabbedPaneWithCloseIcons();

	// This the text area for displaying messages.
	private JTextArea messageTextArea;

	// These are the buttons for managing the selected message.
	private JButton replyButton, forwardButton;

	// This is the JavaMail session.
	private Session session;

	
	private JButton refresh;

	
	// Keeps track of weather a new message is up or not
	MessageDialog testDialog = null;


	// Constructor for E-mail Client.
	public EmailClient2(String u, String p) {
		Pattern emailPattern = Pattern.compile(".@.");
		Matcher emailMatcher = emailPattern.matcher(u);
		if(emailMatcher.find()){
			SqlDBController.setCurrentUser(u);
		}else{
			SqlDBController.setCurrentUser(u + "@gmail.com");
		}
		
		// initialize and create default labels for the side panel labels
		labels = new ArrayList<JLabel>();
		for (int i = 0; i < 4; i++) {
			labels.add(new JLabel());
		}
		// initialize and create default text areas for the side panel
		sideInfo = new ArrayList<JTextArea>();
		for (int i = 0; i < 4; i++) {
			sideInfo.add(new JTextArea());
		}
		// initialize the ArrayList that will hold the messages associated with
		// open tabs
		tabMessages = new ArrayList<ReceivedMessage>();

		username = u;
		password = p;
		// Set application title.
		setTitle("Gmail by Group 7");
		// Set window size.
		setExtendedState(JFrame.MAXIMIZED_BOTH);
		setSize(800, 600);
		// Handle window closing events.
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				actionExit();
			}
		});

		// Setup file menu.
		JMenuBar menuBar = new JMenuBar();
		JMenu fileMenu = new JMenu("File");
		fileMenu.setMnemonic(KeyEvent.VK_F);

		JMenuItem newMessage = new JMenuItem("New Message");
		newMessage.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionNew();
			}
		});
		fileMenu.add(newMessage);
		newMessage.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,
				ActionEvent.CTRL_MASK));

		JMenu tMenu = new JMenu("Thread");

		JMenuItem createThread = new JMenuItem("Create Thread");
		createThread.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				createThread();
			}

		});

		JMenuItem rename = new JMenuItem("Rename Thread");
		rename.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				renameBox();

			}

		});
		JMenuItem merge = new JMenuItem("Merge Thread");
		merge.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				mergeThreads();
			}

		});
		JMenuItem send = new JMenuItem("Send Thread");
		send.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				actionForward();
			}

		});
		
		JMenuItem delete = new JMenuItem("Unthread");
		delete.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				unthread();

			}

		});
		tMenu.add(createThread);
		tMenu.add(rename);
		tMenu.add(merge);
		tMenu.add(send);
		//tMenu.add(search);
		tMenu.add(delete);

		tMenu.setVisible(true);
		fileMenu.add(tMenu);

		JMenuItem fileExitMenuItem = new JMenuItem("Exit");
		fileExitMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionExit();
			}
		});
		fileMenu.add(fileExitMenuItem);
		fileExitMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
				ActionEvent.CTRL_MASK));
		menuBar.add(fileMenu);
		setJMenuBar(menuBar);

		// Setup buttons panel.
		JPanel buttonPanel = new JPanel();
		JButton newButton = new JButton("New Message");
		newButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionNew();
			}
		});
		buttonPanel.add(newButton);

		buttonPanel.setBackground(Color.BLACK);
		// adding open button
		JButton openButton = new JButton("Open");
		openButton.addActionListener(new ActionListener() {
			// /added lance staley

			public void actionPerformed(ActionEvent e) {
				// get the selected row
				int[] rowArray = table.getSelectedRows();
				int i = 0;
				while (i < rowArray.length) {
					System.out.println("IN HERE!!!!!!!!!!!!!!!!!!!");
					// get selected message
					ReceivedMessage selectedMsg = tableModel
							.getMessage(rowArray[i]);
					//----------------------------------------------------
					tabMessages.add(selectedMsg);
					System.out.print(selectedMsg.getSubject());
					System.out.println(tabMessages.size());
					//-----------------------------------------------------

					// get the subject of the message
					String subject = (String) table.getModel().getValueAt(
							rowArray[i], 1);
					String context = " ";
					// get the message content
					try {
						context = getMessageBody(selectedMsg);
					} catch (Exception e1) {
						e1.printStackTrace();
					}
					// make a text area, add the message's content to it.
					JTextArea txt = new JTextArea();
					txt.append(context);
					// make a scroll pane with the text area inside it and add
					// the scroll pane to the tabb pane.
					JScrollPane pane = new JScrollPane(txt);
					tab.add(subject, pane);
					tab.setSelectedIndex(tab.getTabCount() - 1);
					i++;
				}
				System.out.println();
				for (int j = 0; j < tabMessages.size(); j++){
					System.out.println(tabMessages.get(j).getSubject());
				}
				tab.setSelectedIndex(tab.getTabCount() - 1);
				updateSidePanel(tabMessages.get(tab.getTabCount() - 2));
			}
		});
		buttonPanel.add(openButton);

		// Threading button
		// thread button drop down menu setup
		final JPopupMenu threadMenu = new JPopupMenu();

		threadMenu.add(new JMenuItem(new AbstractAction("Create Thread") {

			@Override
			public void actionPerformed(ActionEvent e) {
				createThread();

			}

		}));
		threadMenu.add(new JMenuItem(new AbstractAction("Rename Thread") {

			@Override
			public void actionPerformed(ActionEvent e) {
				renameBox();

			}

		}));
		threadMenu.add(new JMenuItem(new AbstractAction("Merge Thread") {

			@Override
			public void actionPerformed(ActionEvent e) {
				mergeThreads();
			}

		}));
		threadMenu.add(new JMenuItem(new AbstractAction("Send Thread") {

			@Override
			public void actionPerformed(ActionEvent e) {
				actionForward();
			}

		}));
		
		threadMenu.add(new JMenuItem(new AbstractAction("Unthread") {

			@Override
			public void actionPerformed(ActionEvent e) {
				unthread();
			}

		}));
		JButton threading = new JButton("Thread");
		threading.addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent e) {
				threadMenu.show(e.getComponent(), e.getX(), e.getY());
			}
		});
		buttonPanel.add(threading);
		
		//Export button
		JButton export = new JButton("Export");
		export.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent arg0) {
				Exporter ex = new Exporter(getRootPane());
				//if the export attempt is from the inbox
				if (tab.getSelectedIndex() == 0){
					//show error and exit if nothing is selected
					if (table.getSelectedRowCount() == 0){
						JOptionPane.showMessageDialog(getRootPane(), "Nothing is selected to export!");
						return;
					}
					//show error and exit if multiple objects are selected
					else if (table.getSelectedRowCount() > 1){
						JOptionPane.showMessageDialog(getRootPane(), "Can't export multiple items at one time!");
						return;
					}
					
					// get selected message
					ReceivedMessage msg = tableModel.getMessage(table.getSelectedRow());
					// if there is not a thread, it is just a single message
					if (!Util.isThread(msg)){
						ex.exportEmail(msg);
					}
					//if it is a thread
					else {
						//use Tylers method to grab the display for a thread
						String toExport = getMessageBody(msg);
						ex.export(toExport);
					}
				}
				//if the export is called when inside of a tab use what is the tab
				else {
					//grab the current tab index then the email associated with that index
					int currentTab = tab.getSelectedIndex();
					ReceivedMessage msg = tabMessages.get(currentTab - 1);
					// if there is not a thread, it is just a single message
					if (!Util.isThread(msg)){
						ex.exportEmail(msg);
					}
					//if it is a thread
					else{
						String toExport = getMessageBody(msg);
						ex.export(toExport);
					}
				}
			}
		});
		buttonPanel.add(export);
		
		tableSetup();

		// Setup E-mails panel.
		JPanel emailsPanel = new JPanel();
		f5_refresh(emailsPanel);
		emailsPanel.setBackground(Color.DARK_GRAY);
		emailsPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory
				.createEmptyBorder(), "Emails", TitledBorder.ABOVE_TOP,
				TitledBorder.DEFAULT_JUSTIFICATION, Font.getFont("BOLD"),
				Color.WHITE));
		messageTextArea = new JTextArea();
		f5_refresh(messageTextArea);
		messageTextArea.setEditable(false);
		// changed messageTextArea to tab @lance staley
		emailsPanel.setLayout(new BorderLayout());
		emailsPanel.add(tab, BorderLayout.CENTER);

		// Setup buttons panel 2.
		JPanel buttonPanel2 = new JPanel();
		replyButton = new JButton("Reply");
		replyButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionReply();
			}
		});
		replyButton.setEnabled(false);
		buttonPanel2.add(replyButton);
		forwardButton = new JButton("Forward");
		forwardButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionForward();
			}
		});
		forwardButton.setEnabled(false);
		buttonPanel2.add(forwardButton);

		infoPanel = new JPanel(new GridLayout(8, 2));
		infoPanel.setBackground(Color.DARK_GRAY);
		infoPanel.setPreferredSize(new Dimension(300, 800));

		// Add panels to display.
		getContentPane().setLayout(new BorderLayout());
		getContentPane().add(buttonPanel, BorderLayout.NORTH);
		getContentPane().add(infoPanel, BorderLayout.EAST);
		getContentPane().add(emailsPanel, BorderLayout.CENTER);
		getContentPane().add(buttonPanel2, BorderLayout.SOUTH);

		setupSidePanel();
		clearSidePanel();
	}
	/**
	 * Function to create a thread
	 * @author Burnham
	 */
	public void createThread() {
		String name = JOptionPane.showInputDialog(getContentPane(),
				"Enter name: ", "Create Thread", JOptionPane.QUESTION_MESSAGE);
		
		if(name != null && !name.equals("")){
			
			
			ArrayList<ReceivedMessage> emails = new ArrayList<ReceivedMessage>();
			int[] rows = table.getSelectedRows();
			ReceivedMessage msg = new ReceivedMessage();
			if(rows.length > 0){
				msg = getSelectedMessage();
				if(ThreadManager.getInstance().getThread(Util.createId(name+msg.getFrom()+msg.getDate())) != null){
					JOptionPane.showMessageDialog(getContentPane(), "Thread already created with this name. Please select a unique name.");
					return;
				}
			}
			
			for (int row = 0; row < rows.length; row++) {
				ReceivedMessage message = tableModel.getMessage(rows[row]);
				// if the email isn't a thread, add it
				if(!Util.isThread(message)){
					emails.add(message);
				}else{
					// else alert user that it is alread a thread
					JOptionPane.showMessageDialog(getContentPane(), "Please use Merge Thread as a thread has been selected to create a thread");
					return;
				}
				
			}
			emails = Util.getSortedEmails(emails);
			// create a thread with ThreadManager
			long id = Util.createId(name+msg.getFrom()+msg.getDate());
			ThreadManager.getInstance().createThread(id, name, emails);
			// remove all the lower selected emails
			for (int row = rows.length - 1; row > 0; row--) {
				int rw = rows[row]; 
				tableModel.deleteMessage(rw);
			}
			// create the dummy thread email
			tableModel.createThreadMessage(rows[0], name);
		}
		// empty name
		else if(name != null){
			JOptionPane.showMessageDialog(getContentPane(), "Please enter a name for the new thread.");
			createThread();
		}
		// else cancel called
	}

	/**
	 * Simple function to rename a thread.
	 * @author Burnham
	 */
	public void renameBox() {
		// not a thread
		ReceivedMessage msg = getSelectedMessage();
		if(!Util.isThread(msg)){
			JOptionPane.showMessageDialog(getContentPane(), "Selected item is not a thread. Please select a thread to rename.");
			return;
		}
		// grab the name
		String newName = JOptionPane.showInputDialog(getContentPane(), "Enter name: ","Rename Thread",JOptionPane.QUESTION_MESSAGE);
		// if it is not null or an empty string
		if((newName != null) && !newName.equals("")){
			
			int row = table.getSelectedRow();
			ReceivedMessage message = getSelectedMessage();
			if(Util.isThread(message)){
				long id = Util.createId(message.getSubject()+message.getFrom()+message.getDate());
				long newid = Util.createId(newName+message.getFrom()+message.getDate());
				ThreadManager.getInstance().renameThread(id, newid, newName);
				tableModel.createThreadMessage(row, newName);
			}
		}
		// if it is an empty string, re-enter this function again until they provide
		// a new name or press cancel
		else if(newName.equals("")){
			JOptionPane.showMessageDialog(getContentPane(), "Please provide a name");
			renameBox();
		}
		// else cancel was pressed
	}

	/**
	 * Function to untread a thread
	 * @author Burnham
	 */
	public void unthread() {
		int option = JOptionPane.showConfirmDialog(null,
				"Are you sure you want to unthread?", "Unthread",
				JOptionPane.YES_NO_CANCEL_OPTION);
		if (option == JOptionPane.YES_OPTION) {
			if(table.getSelectedRowCount() > 1){
				JOptionPane.showMessageDialog(getContentPane(), "Please select only one Thread to unthread at a time.");
				return;
			}
			// clicked yes
			int row = table.getSelectedRow();
			ReceivedMessage slt = tableModel.getMessage(row);
			long id = Util.createId(slt.getSubject()+slt.getFrom()+slt.getDate());
			// grab the thread, null if it is a single email
			Thread t = ThreadManager.getInstance().getThread(id);
			if(t == null){
				JOptionPane.showMessageDialog(getContentPane(), "Can't un-thread a single email.");
				return;
			}
			try{
				// delete this entry
				tableModel.deleteMessage(row);
			}catch(Exception e){}
			// grab the emails and set the messages at the beginning
			ArrayList<ReceivedMessage> emails = t.getEmails();
			for(int i = 0; i < emails.size(); i++){
				// append to the end
				tableModel.setMessage(-1, emails.get(i));
			}
			
			// sort the inbox by date
			ArrayList<ReceivedMessage> messages = Util.getSortedEmails(tableModel.getAllMessages());
			// set the messages again
			tableModel.setMessages(messages);
			table.revalidate();
			table.repaint();
			// call to make sure this thread is removed
			ThreadManager.getInstance().unthread(id);
			
		}
		// else no or cancel is clicked
	}
	/**
	 * Function for Merging a thread or email into a thread
	 * @author Burnham
	 */
	public void mergeThreads(){
		// check to see we there are 2 things selected
		if(table.getSelectedRowCount() < 2){
			JOptionPane.showMessageDialog(getContentPane(), "Please select at least another email or thread to merge with.");
		}else{
			// grab all the rows
			int[] rows = table.getSelectedRows();
			Thread mergeTo = null;
			ArrayList<ReceivedMessage> messagesToAdd = new ArrayList<ReceivedMessage>();
			ArrayList<Long> removeTheseThreads = new ArrayList<Long>();
			boolean topLevelThreadFound = false;
			// loop through all the rows and grab the ReceivedMessage
			for(int i = 0; i < rows.length; i++){
				ReceivedMessage message = tableModel.getMessage(rows[i]);
				// if it is a thread
				if(Util.isThread(message)){
					long id = Util.createId(message.getSubject()+message.getFrom()+message.getDate());
					// grab the thread based on the id above
					Thread thread = ThreadManager.getInstance().getThread(id);
					// if we haven't found a thread yet, use this thread as top level thread to merge to
					if(!topLevelThreadFound){
						mergeTo = thread;
						topLevelThreadFound = true;
						removeTheseThreads.add(mergeTo.getId());
					}else{
						// else not this thread to remove
						removeTheseThreads.add(thread.getId());
					}
					// grab the emails from the thread
					ArrayList<ReceivedMessage> toMerge = thread.getEmails();
					// add them to the list
					for(int j = 0; j < toMerge.size(); j++){
						messagesToAdd.add(toMerge.get(j));
					}
				}
				// it is just a single email, so add it
				else{
					messagesToAdd.add(message);
				}
			}
			// if we didn't find any thread, ask the user to create a thread instead
			if(mergeTo == null){
				JOptionPane.showMessageDialog(getContentPane(), "No thread was selected to merge to, please create a thread with the messages.");
			}else{
				long oldId = mergeTo.getId();
				ReceivedMessage msg = tableModel.getMessage(rows[0]);
				// call to merge the threads
				long newId = Util.createId(mergeTo.getName()+msg.getFrom()+msg.getDate());
				if(oldId != newId){
					mergeTo.setId(newId);
					ThreadManager.getInstance().updateThread(mergeTo.getId(), mergeTo);
					ThreadManager.getInstance().removeThread(oldId);
					removeTheseThreads.add(oldId);
				}
				ThreadManager.getInstance().mergeThreads(mergeTo.getId(),
														 Util.getSortedEmails(messagesToAdd), 
														 removeTheseThreads);
				// delete all rows except the first entry
				for(int i = rows.length-1; i > 0; i--){
					tableModel.deleteMessage(rows[i]);
				}
				// use the first entry to update the new thread position
				tableModel.createThreadMessage(rows[0], mergeTo.getName());
			}
		}
	}
	
	/**
	 * Setup the inbox table and the listeners for selecting
	 * a message and open it in a new tab.
	 */
	public void tableSetup() {
		// Setup messages table.

		tableModel = new CustomMessageTableModel();
		table = new JTable(tableModel);
		JScrollPane pane = new JScrollPane(table);
		//f5_refresh(table);
		//f5_refresh(tab);
		tab.addNonClosableTab("Inbox", pane);
		//tab.addNonClosableTab("Inbox", table);

		tab.setBackground(Color.WHITE);
		tab.setBorder(BorderFactory.createEtchedBorder(Color.BLACK, Color.RED));

		// set the tab to visible and add a mouse listener @lance staley
		tab.setVisible(true);
		table.addMouseListener(new MouseListener() {

			@Override
			public void mouseReleased(MouseEvent e) {
			}

			@Override
			public void mousePressed(MouseEvent e) {

			}

			@Override
			public void mouseExited(MouseEvent e) {
			}

			@Override
			public void mouseEntered(MouseEvent e) {
			}

			@Override
			public void mouseClicked(MouseEvent e) {
				// @lance staley
				// check to see if click is left-click
				if ((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK) {
					if(e.getClickCount() < 2){
						updateButtons();
					}
					// check for a double click
					else if (e.getClickCount() >= 2) {

						// get selected message
						ReceivedMessage selectedMsg = tableModel.getMessage(table.getSelectedRow());
						tabMessages.add(selectedMsg);
						// get the subject of the message
						String subject = selectedMsg.getSubject();
						// temp.getModel().setValueAt(row,1, 0);
						String context = getMessageBody(selectedMsg);
						// make a text area, add the message's content to
						// it.
						JTextArea txt = new JTextArea();
						txt.append(context);
						txt.setEditable(false);
						// make a scroll pane with the text area inside it
						// and
						// add the scroll pane to the tabb pane.
						JScrollPane pane = new JScrollPane(txt);
						tab.add(subject, pane);
						// highlights new tab that is opened when email is
						// double-clicked
						tab.setSelectedIndex(tab.getTabCount() - 1);

					}
				}
				// check to see if click is mouse-wheel click
				if ((e.getModifiers() & InputEvent.BUTTON2_MASK) == InputEvent.BUTTON2_MASK) {

				}
				// if this check is reached, the click was a right-click
				else {

				}
			}
		});
		
		// Allow multiple emails to be selected.
		table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);

	}

	public void f5_refresh(JComponent o) {
		o.addKeyListener(new KeyListener() {
			// TODO: what to do here, need to just remove this code?
			@Override
			public void keyPressed(KeyEvent e) {
				if (e.getKeyCode() == KeyEvent.VK_F5) {
					tab.remove(0);
					tableSetup();
					mailSetup();
				}

			}

			@Override
			public void keyReleased(KeyEvent e) {
				// do nothing

			}

			@Override
			public void keyTyped(KeyEvent e) {
				// do nothing

			}

		});
	}

	// Exit this program.
	private void actionExit() {
		System.exit(0);
	}

	// Create a new message.
	private void actionNew() {
		if (testDialog == null){
			sendMessage(MessageDialog.NEW, null);
		}
		else if (testDialog.isVisible()){
			return;
		}
		else{
			sendMessage(MessageDialog.NEW, null);
		}
	}

	// Reply to a message.
	private void actionReply() {
		if (tab.getSelectedIndex() == 0){
			//show error and exit if nothing is selected
			if (table.getSelectedRowCount() == 0){
				JOptionPane.showMessageDialog(getRootPane(), "Nothing is selected to reply to!");
				return;
			}
			//show error and exit if multiple objects are selected
			else if (table.getSelectedRowCount() > 1){
				JOptionPane.showMessageDialog(getRootPane(), "Can't reply to multiple items at one time!");
				return;
			}
			
			// get selected message
			ReceivedMessage msg = tableModel.getMessage(table.getSelectedRow());
			
			if (Util.isThread(msg)){
				//get the thread that is represented by the received message
				long id = Util.createId(msg.getSubject()+msg.getFrom()+msg.getDate());
				ThreadManager manager = ThreadManager.getInstance();
				Thread thread = manager.getThread(id);
				
				ReplyBox box = new ReplyBox(thread, this);
//				box.setVisible(true);
			}
			else{
				//create and display mesage dialog
				MessageDialog dialog = new MessageDialog(this, 1, msg);
				dialog.setVisible(true);
			}
		}
		else {
			//get the message in the the tab
			ReceivedMessage msg = tabMessages.get(tab.getSelectedIndex() - 1);
			
			if (Util.isThread(msg)){
				//get the thread that is represented by the received message
				long id = Util.createId(msg.getSubject()+msg.getFrom()+msg.getDate());
				ThreadManager manager = ThreadManager.getInstance();
				Thread thread = manager.getThread(id);
				
				ReplyBox box = new ReplyBox(thread, this);
			}
			else{
				//create and display mesage dialog
				MessageDialog dialog = new MessageDialog(this, 1, msg);
				dialog.setVisible(true);
			}
		}
	}

	// Forward a message.
	private void actionForward() {
		if (tab.getSelectedIndex() == 0){
			//show error and exit if nothing is selected
			if (table.getSelectedRowCount() == 0){
				JOptionPane.showMessageDialog(getRootPane(), "Nothing is selected to forward!");
				return;
			}
			//show error and exit if multiple objects are selected
			else if (table.getSelectedRowCount() > 1){
				JOptionPane.showMessageDialog(getRootPane(), "Can't forward multiple items at one time!");
				return;
			}
			
			// get selected message
			ReceivedMessage msg = tableModel.getMessage(table.getSelectedRow());
			//create and display mesage dialog
			MessageDialog dialog = new MessageDialog(this, 2, msg);
			dialog.setVisible(true);
		}
		else {
			//grab the message from current tab
			int tabIndex = tab.getSelectedIndex();
			//create and display the message dialog, -1 to acount for 0 being the inbox
			MessageDialog dialog = new MessageDialog(this, 2, tabMessages.get(tabIndex - 1));
			dialog.setVisible(true);
		}
	}

	// Send the specified message.
	private void sendMessage(int type, ReceivedMessage message) {
		MessageDialog dialog = new MessageDialog(this, type, message);
		testDialog = dialog;
		dialog.setVisible(true);
	}

	/**
	 * creates the initial setup for the side panel
	 * @author Jamison
	 */
	private void setupSidePanel() {
		// Initialize the sideScroll array
		sideScroll = new ArrayList<JScrollPane>();

		infoPanel.removeAll();
		// create from line
		JLabel from = labels.get(0);
		from.setText("From: ");
		from.setForeground(Color.WHITE);
		// create from text box
		sideInfo.get(0).setEditable(false);
		JScrollPane fromPane = new JScrollPane(sideInfo.get(0));
		sideScroll.add(fromPane);

		// create subject line
		JLabel subject = labels.get(1);
		subject.setText("Subject: ");
		subject.setForeground(Color.WHITE);
		// create subject text box
		sideInfo.get(1).setEditable(false);
		JScrollPane subjectPane = new JScrollPane(sideInfo.get(1));
		sideScroll.add(subjectPane);

		// create date line
		JLabel date = labels.get(2);
		date.setText("Date: ");
		date.setForeground(Color.WHITE);
		// create date text box
		sideInfo.get(2).setEditable(false);
		JScrollPane datePane = new JScrollPane(sideInfo.get(2));
		sideScroll.add(datePane);

		// create recipients line
		JLabel recipients = labels.get(3);
		recipients.setText("Recipients: ");
		recipients.setForeground(Color.WHITE);
		// create recipients text area
		sideInfo.get(3).setEditable(false);
		JScrollPane recipientsPane = new JScrollPane(sideInfo.get(3));
		sideScroll.add(recipientsPane);

		infoPanel.add(from);
		infoPanel.add(sideScroll.get(0));
		infoPanel.add(subject);
		infoPanel.add(sideScroll.get(1));
		infoPanel.add(date);
		infoPanel.add(sideScroll.get(2));
		infoPanel.add(recipients);
		infoPanel.add(sideScroll.get(3));
	}

	/**
	 * Getter for the first selected message.
	 * @return
	 */
	public static ReceivedMessage getSelectedMessage(){
		ReceivedMessage message = tableModel.getMessage(table.getSelectedRow());
		return message;
	}
	
	/**
	 * Updates the side panel to display the info of the email that is currently
	 * being viewed
	 * 
	 * @param m
	 *            the ReceivedMessage that is associated with the open tab
	 * @author Jamison
	 */
	public static void updateSidePanel(ReceivedMessage m) {
		// make sure that the panel info isn't hidden
		setVisiblePanel();

		// update the info in the text boxes for an Email
		sideInfo.get(0).setText(m.getFrom());
		sideInfo.get(1).setText(m.getSubject());
		sideInfo.get(2).setText(m.getDate());
		sideInfo.get(3).setText(formatRecipients(m.getRecipients()));
	}

	private static String formatRecipients(String s) {
		String ret = "";
		int prev = 0;
		if (s != null) {
			for (int i = 0; i < s.length(); i++) {
				if (s.charAt(i) == ',') {
					ret += s.substring(prev, i + 1) + '\n';
					prev = i + 2;
				}
			}
			return ret;
		} else {
			return "";
		}
	}

	/**
	 * Hides all of the info in the side panel so that is isn't visible
	 * 
	 * @author Jamison
	 */
	public static void clearSidePanel() {
		// hide the scroll panes containing the text areas
		sideScroll.get(0).setVisible(false);
		sideScroll.get(1).setVisible(false);
		sideScroll.get(2).setVisible(false);
		sideScroll.get(3).setVisible(false);

		// hide the labels
		labels.get(0).setVisible(false);
		labels.get(1).setVisible(false);
		labels.get(2).setVisible(false);
		labels.get(3).setVisible(false);
	}

	/**
	 * Unhides all of the info in the side panel
	 * 
	 * @author Jamison
	 */
	public static void setVisiblePanel() {
		// unhide the scroll panes containing the text areas
		sideScroll.get(0).setVisible(true);
		sideScroll.get(1).setVisible(true);
		sideScroll.get(2).setVisible(true);
		sideScroll.get(3).setVisible(true);

		// unhide the labels
		labels.get(0).setVisible(true);
		labels.get(1).setVisible(true);
		labels.get(2).setVisible(true);
		labels.get(3).setVisible(true);
	}

	/**
	 * Function to get the message body whether it is a thread or if it is a
	 * single message. Added by Tyler Burnham
	 * 
	 * @return
	 */
	public static String getMessageBody(ReceivedMessage msg) {
		String body = "";
		try {
			// call the hashing function to get the potential id of a thread
			long id = Util.createId(msg.getSubject()+msg.getFrom()+msg.getDate());
			// if there is not a thread, it is just a single message
			if (ThreadManager.getInstance().getThread(id) == null) {
				body = msg.getBody();
			} else {
				// grab the thread since we found one
				Thread t = ThreadManager.getInstance().getThread(id);
				// get the emails of that thread
				ArrayList<ReceivedMessage> emails = t.getEmails();
				// append inline to the body all the information
				for (int i = 0; i < emails.size(); i++) {
					ReceivedMessage message = emails.get(i);
					body += ("From: " + message.getFrom() + "\n");
					body += ("Subject: " + message.getSubject() + "\n");
					body += ("Date: " + message.getDate() + "\n");
					body += ("\n");
					body += (message.getBody() + "\n");
					if(i < emails.size()-1){
						body += ("----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n");
					}else{
						body += ("\n");
					}
				}
			}
//			// update the message panel, not sure if this needs to be done
//			if (Util.isThread(getSelectedMessage())){
//				clearSidePanel();
//			}
//			else{
//				updateSidePanel(getSelectedMessage());
//			}
		} catch (Exception e) {
			e.printStackTrace();
			//showError("Unabled to load message.", false);
		}
		return body;
	}

	/*
	 * Update each button's state based off of whether or not there is a message
	 * currently selected in the table.
	 */
	private void updateButtons() {
		if (getSelectedMessage() != null) {
			replyButton.setEnabled(true);
			forwardButton.setEnabled(true);
		} else {
			replyButton.setEnabled(false);
			forwardButton.setEnabled(false);
		}
	}

	public static DownloadingDialog a = new DownloadingDialog(new JFrame());
	
	public Properties props;
	
	/**
	 * Set the properties and call to connect to load
	 * the messages from the user's inbox.
	 */
	public void mailSetup() {
		Properties props = new Properties();
		props.setProperty("mail.imap.host", "imap.gmail.com");
		props.setProperty("mail.imap.port", "993");
		props.setProperty("mail.imap.connectiontimeout", "5000");
		props.setProperty("mail.imap.timeout", "5000");
		props.setProperty("mail.imap.socketFactory.class",
				"javax.net.ssl.SSLSocketFactory");
		props.setProperty("mail.imap.socketFactory.fallback", "false");
		props.setProperty("mail.smtp.host", "smtp.gmail.com");
		props.setProperty("mail.smtp.port", "465");
		props.put("mail.debug", "true");
		this.props = props;
		(new BackThread()).start();
	}

	/**
	 * This code is taken in part from the following URL:
	 * http://stackoverflow.com
	 * /questions/5928608/reading-the-full-email-from-gmail-using-javamail
	 */
	public static int messageIndex = 0;
	public static int messageLength = 0;
	
	// Connect to e-mail server.
	public void connectToMail(Properties props) {
		// Establish JavaMail session and connect to server.
		Store store = null;
		try {
			session = Session.getDefaultInstance(props, null);
			// Connect to e-mail server.
			store = session.getStore("imaps");
			store.connect("imap.gmail.com", username, password);
		} catch (Exception e) {
			// Show error dialog.
			showError("Unable to connect.", true);
		}
		// Download message headers from server.
		try {
			System.out.println("Opening inbox");
			// Open main "INBOX" folder.
			Folder folder = store.getFolder("INBOX");
			folder.open(Folder.READ_WRITE);

			// Get folder's list of messages.
			Message[] messages = folder.getMessages();
			

			// Retrieve message headers for each message in folder.
			FetchProfile profile = new FetchProfile();
			profile.add(FetchProfile.Item.ENVELOPE);
			folder.fetch(messages, profile);

			// -------Addition by Jamison, Tyler---------//
			// creating an ArrayList of ReceivedMessages so from the array of
			// messages so that we can
			// use the methods in the ReceivedMessage class
			ArrayList<ReceivedMessage> theMessages = new ArrayList<ReceivedMessage>();
			messageLength = messages.length;
			for (int i = 0; i < messages.length; i++) {
						ReceivedMessage ourmessage = new ReceivedMessage(messages[i],Util.createId(messages[i]));
						messageIndex++;
				try {
					// check if our message starts with Re: for a reply thread
					if (ourmessage.getSubject().toUpperCase().startsWith("RE:")) {
						// create an id as if it were a thread
						long id = Util.createId(ourmessage.getSubject());
						// if there is a thread already created for this reply
						Thread t = ThreadManager.getInstance().getThread(id);
						if (t != null) {
							// just add the email
							ThreadManager.getInstance().getThread(id).addEmail(ourmessage);
						} else {
							// create a new ReplyThread which just sets a flag
							// in Thread to true
							ArrayList<ReceivedMessage> threadMessages = new ArrayList<ReceivedMessage>();
							threadMessages.add(ourmessage);
							ThreadManager.getInstance().createReplyThread(id,ourmessage.getSubject(), threadMessages);
						}
					}
				} catch (Exception e) {
					// do nothing, subject was empty or something so it will be
					// an individual message
				}
				theMessages.add(ourmessage);
			}
			
			// Put messages in table.
			tableModel.setMessages(theMessages);

			// display previous threads and reply threads
			Thread[] threads = ThreadManager.getInstance().getThreads();
			// for each thread previously created including reply threads from
			// above
			for (Thread t : threads) {
				// adding handling for adding the first message with the same subject as a reply
				if(t.isReplyThread()){
					// grab only the part of the subject without a reply
					String original = t.getName().substring(4);
					// grab the messages
					ArrayList<ReceivedMessage> allemails = tableModel.getAllMessages();
					// loop through until we find an email with the same subject
					for(int i = 0; i < allemails.size(); i++){
						long ourid = Util.createId(original);
						long theirid = Util.createId(allemails.get(i).getSubject());
						// add it to the list of emails, then exit the loop
						if(ourid == theirid){
							t.addEmail(allemails.get(i), 0);
							break;
						}
					}
				}
				t.setEmails(Util.getSortedEmails(t.getEmails()));
				
				// grab the emails
				ArrayList<ReceivedMessage> emails = t.getEmails();
				ReceivedMessage specificEmail = emails.get(emails.size()-1);
				
				// count is for determining if we should remove or what to do
				int count = 0;
				// for all the emails in the thread
				for (ReceivedMessage msg : emails) {
					// if it is a reply thread and the count != 0
					if (t.isReplyThread() && count != 0) {
						// we delete it, otherwise we don't want to delete it
						tableModel.deleteMessage(msg);
					} else if (!t.isReplyThread()) {
						// we want to create a dummy email with the subject set
						// to the thread name
						if (count == t.getEmails().size() - 1) {
							tableModel.createThreadMessage(msg, t.getName());
						}
						// else we delete the message
						else {
							tableModel.deleteMessage(msg);
						}
					}else{
						long oldId = t.getId();
						String subject = msg.getSubject();
						if(!msg.getSubject().startsWith("Re:")){
							msg.setSubject("Re: "+subject);
							t.setId(Util.createId("Re: "+subject+msg.getFrom()+msg.getDate()));
						}else{
							t.setId(Util.createId(subject+msg.getFrom()+msg.getDate()));
						}
						
						ThreadManager.getInstance().updateThread(t.getId(), t);
						ThreadManager.getInstance().removeThread(oldId);
					}
					// increment count
					count++;
				}
			}

		} catch (Exception e) {
			// Close the downloading dialog.
			e.printStackTrace();
			// Show error dialog.
			showError("Unable to download messages.", true);
		}
		// splitPane.setDividerLocation(0.5);
		//a.setVisible(false);
	}

	// Show error dialog and exit afterwards if necessary.
	private void showError(String message, boolean exit) {
		JOptionPane.showMessageDialog(this, message, "Error",
				JOptionPane.ERROR_MESSAGE);
		if (exit)
			System.exit(0);
	}

	// Run the E-mail Client.
	public static void main(String[] args) {
		// get the parameters of the database system
		String DBURL = "jdbc:mysql://";
		String DBUSER = "";
		String DBPASSWORD = "";
		try{
			String host = args[0] + ":3306";
			DBURL += host;
		}catch(Exception e){
			DBURL = "jdbc:mysql://localhost:3306";
		}
		SqlDBController.setUrl(DBURL);
		
		try{
			DBUSER = args[1];
		}catch(Exception e){
			DBUSER = "root";
		}
		SqlDBController.setDbUsername(DBUSER);
		
		try{
			DBPASSWORD = args[2];
		}catch(Exception e){
			DBPASSWORD = "";
		}
		SqlDBController.setDbPassword(DBPASSWORD);
		
		try {
			login = new LoginPrompt();
			login.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			
			// used for centering login box
			login.setLocationRelativeTo(null);
			login.setVisible(true);
		} catch (Exception e) {
			JOptionPane.showMessageDialog(null, e.getMessage());
		}
	}
	
	public static boolean flag = false;
	class BackThread extends java.lang.Thread{
		
		public void run(){
			java.lang.Thread popThread = new java.lang.Thread(new PopUpThread());
			popThread.start();
			connectToMail(props);
			if(EmailClient2.messageIndex == EmailClient2.messageLength){
				try {
					java.lang.Thread.sleep(1000);
				} catch (InterruptedException e) {
					
				}
			}
			a.setVisible(false);
			popThread.interrupt();
			setVisible(true);
		}
	}
	
	class PopUpThread extends java.lang.Thread{
		
		public void run(){
			try{
				a.setVisible(true);
				a.setFocusableWindowState(true);
				int i = 0;
				while(i<599999999){
					java.lang.Thread.sleep(1);
					a.refreshLabel();
					i++;
				}
			}
			catch(InterruptedException e){
				return;
			}
			catch(Exception e){
				e.printStackTrace();
			}
		}
		
	}

}
